Skip to content

Conversation

@aryehklein
Copy link
Contributor

Description

This PR comes to solve #9366.

Changes Made

  • Adds support for a new pydantic field use_annotated_field_aliases that generates the pydantic types needed

Testing

  • Unit tests added/updated
  • Manual testing completed


use_pydantic_field_aliases: bool = True

use_annotated_field_aliases: bool = False
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level points:

  1. If this looks good, I assume I also need a docs change - can you point me to where to make that?
  2. I added unit tests for this change, but didnt know how/where to do an end to end test with a full on generation?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great questions!

  1. We document the pydantic config in a separate repo specifically for our docs. You can see an analogous feature flag docs PR here: Document lazy_imports (Python SDK) docs#702. We're happy to make that PR though if you'd prefer! You've already been so generous with your time on this contribution.
  2. Our strategy for most ETE testing these days uses our framework called seed, which is a regression testing framework that you can read more about in CONTRIBUTING.md.
    • For testing this, I'd probably recommend adding a new alias-extends fixture (could also use exhaustive) with this custom config set
    • You'd then generate and check in the seed files pnpm seed:local test --generator python-sdk --fixture alias-extends --outputFolder <whatever_you_name_the_output_folder>. This will also run ruff and mypy against the files
    • If you want more test coverage against the SDK, you can check in additional test files to the generated directory and .fernignore them

# Use only the default value as initializer, not pydantic.Field
initializer = default_value

elif is_aliased and not self._use_pydantic_field_aliases:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bit of complexity here with how this new feature interplays with use_pydantic_field_aliases. If use_pydantic_field_aliases is false, and _use_annotated_field_aliases is true, you get into double annotated types. The FastAPI generator defaults this to true, whereas the use_pydantic_field_aliases defaults to false. I think this if/elif block handles it, but lmk if there are other edge cases to consider!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know this is reasonable! I'm good with the approach of just preferring it over the other flag (rather than e.g. breaking the config shape), as long as we document that that's the behavior.

datetime: typing.Optional[dt.datetime] = None
date: typing.Optional[dt.date] = None
uuid_: typing_extensions.Annotated[typing.Optional[uuid.UUID], pydantic.Field(alias="uuid")] = None
base_64: typing_extensions.Annotated[typing.Optional[str], pydantic.Field(alias="base64")] = None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjb9dc This is a key line to look at to understand the diff. This line differs from the existing ones (e.g. https://github.com/fern-api/fern/blob/main/seed/fastapi/exhaustive/pydantic-v2/resources/types/resources/object/types/object_with_optional_field.py#L24) and is the core thing I want to solve.

@aryehklein aryehklein requested a review from tjb9dc September 16, 2025 19:34
# Use only the default value as initializer, not pydantic.Field
initializer = default_value

elif is_aliased and not self._use_pydantic_field_aliases:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I know this is reasonable! I'm good with the approach of just preferring it over the other flag (rather than e.g. breaking the config shape), as long as we document that that's the behavior.

Comment on lines +255 to +265
def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]:
maybe_annotated_type = _get_annotation(type_)

if maybe_annotated_type is not None:
# The actual annotations are 1 onward, the first is the annotated type
annotations = typing_extensions.get_args(maybe_annotated_type)[1:]

for annotation in annotations:
if isinstance(annotation, FieldMetadata) and annotation.alias is not None:
return annotation.alias
return None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do wonder how/if this code will interact with this change. It's called (transitively) from the pydantic_utilities files, and we use different ones depending on the aliasing mode we're in. That decision (also factors in v1 vs v2) lives in src/fern_python/generators/core_utilities/core_utilities.py.

I think it's worth testing serializing some of your test models and making sure things look expected. From reading the code I think it's possible that we'll need to use the aliased pydantic utilities, which would mean updating the conditional statement in the three core_utilities.py files to provide that version if using the new feature flag you're adding. But definitely easier to figure that out by testing :)

@github-actions
Copy link

This PR is stale because it has been open 25 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale This PR hasn't has any commits or comments in 25 days or more. label Oct 13, 2025
@github-actions
Copy link

This PR was closed because it has been inactive for 5 days after being marked stale.

@github-actions github-actions bot closed this Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Stale This PR hasn't has any commits or comments in 25 days or more.

Development

Successfully merging this pull request may close these issues.

2 participants